﻿
//#define ONLINE_CANLAUNCH

#ifndef ONLINE_CANLAUNCH
    #pragma once
    #include "sfinae_detail.h"
#endif



#define dSFINAE_ARG1 std::declval<A1>()
#define dSFINAE_ARG2 dSFINAE_ARG1, std::declval<A2>()
#define dSFINAE_ARG3 dSFINAE_ARG2, std::declval<A3>()

#define dSFINAE_CHECK(member_name, ... )                                      \
template<class U>static                                                       \
    typename ::tools::sfinae::Select< C, R,                                   \
        decltype( std::declval<U>().member_name(__VA_ARGS__) ) >::Result      \
            Check( decltype( std::declval<U>().member_name(__VA_ARGS__) )* ); \
                                                                              \
template<class U>static void Check(...);                                      \
typedef decltype( Check<C>(0) )       NoConst;                                \
typedef decltype( Check<const C>(0) ) Const;                                  \
enum { eMETHOD  = std::is_same<NoConst, C>::value };                          \
enum { eCMETHOD = std::is_same<Const,   C>::value };                          \
enum { eDATA = 0 };

#define DETECTED_MEMBER_BY_NAME(member_name)                       \
template<class C, class T>                                         \
struct has_by_name_##member_name                                   \
{                                                                  \
    template<class U>static                                        \
        typename ::tools::sfinae::Select<C, T, decltype(           \
            std::declval<U>().member_name ) >::Result              \
                Check( decltype(std::declval<U>().member_name)* ); \
    template<class U>static void Check(...);                       \
    typedef decltype( Check<C>(0) ) Type;                          \
    enum { eDATA = std::is_same<Type, C>::value };                 \
};                                                                 \
                                                                   \
template<class C, class R>                                         \
struct has_by_name_##member_name<C, R()>                           \
{ dSFINAE_CHECK(member_name); };                                   \
                                                                   \
template<class C, class R,class A1>                                \
struct has_by_name_##member_name<C, R(A1)>                         \
{ dSFINAE_CHECK(member_name, dSFINAE_ARG1); };                     \
                                                                   \
template<class C, class R,class A1,class A2>                       \
struct has_by_name_##member_name<C, R(A1,A2)>                      \
{ dSFINAE_CHECK(member_name, dSFINAE_ARG2); };                     \
                                                                   \
template<class C, class R,class A1,class A2,class A3>              \
struct has_by_name_##member_name<C, R(A1,A2,A3)>                   \
{ dSFINAE_CHECK(member_name, dSFINAE_ARG3); };

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------

#define available_data(class_name, member_type, member_name)     \
    has_by_name_##member_name<class_name, member_type>::eDATA 

#define can_launch_method(class_name, member_type, member_name)  \
    has_by_name_##member_name<class_name, member_type>::eMETHOD

#define can_launch_cmethod(class_name, member_type, member_name) \
    has_by_name_##member_name<class_name, member_type>::eCMETHOD

//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------

#ifdef ONLINE_CANLAUNCH

#include <type_traits>
#include <utility>

namespace tools{
    namespace sfinae{

        template<class U, U u> struct Helper{};
        //------------------------------------------------------

        template<class C, class T, class U> struct Select { typedef void  Result; };
        template<class C, class T> struct Select<C,T,T>   { typedef C     Result; };

        //------------------------------------------------------
        template<class C, class T> struct Transform;
        template<class C, class R> struct Transform<C,R()>
        {
            typedef R(C::*Method)();
            typedef R(C::*CMethod)()const;
        };
        template<class C, class R,class A1> struct Transform<C,R(A1)> 
        {
            typedef R(C::*Method)(A1);
            typedef R(C::*CMethod)(A1)const;
        };
        template<class C, class R,class A1,class A2> struct Transform<C,R(A1,A2)>  
        {
            typedef R(C::*Method)(A1,A2);
            typedef R(C::*CMethod)(A1,A2)const;
        };
        template<class C, class R,class A1,class A2,class A3> struct Transform<C,R(A1,A2,A3)>
        {
            typedef R(C::*Method)(A1,A2,A3);
            typedef R(C::*CMethod)(A1,A2,A3)const;
        };
        //------------------------------------------------------

    }//namespace sfinae
}//namespace tools

int g;
struct A
{
    A():r(g){}

    void fooA(int);
    void fooA(int)const;
    void fooA();
    void fooA()const;

    void foo();
    void foo()const;

    int v;
    int& r;
    int a[10];
    int* p;
    int** pp;
private:
    void private_foo();
};

struct B : A
{
    int v;
    int fooB(){ return 0; }
};

DETECTED_MEMBER_BY_NAME(foo) ;
DETECTED_MEMBER_BY_NAME(fooA);
DETECTED_MEMBER_BY_NAME(fooB);
DETECTED_MEMBER_BY_NAME(v);
DETECTED_MEMBER_BY_NAME(r);
DETECTED_MEMBER_BY_NAME(a);
DETECTED_MEMBER_BY_NAME(p);
DETECTED_MEMBER_BY_NAME(pp);
DETECTED_MEMBER_BY_NAME(private_foo);

#include<iostream>
using namespace std;

int main()
{
    //синтаксис:  
    
    // вернет true если мембер доступен и является данными
    // available_data(  имя_класса ,  тип_мембера  , имя_мембера)
    
    // вернет true если можно запустить метод по указанному протоколу, через неконстантное имя
    // can_launch_method(  имя_класса ,  тип_мембера  , имя_мембера)
    
    //  вернет true если можно запустить метод по указанному протоколу, через константное имя
    //  is_available_cmethod(  имя_класса ,  тип_мембера  , имя_мембера)

    //---- детектирование данных
    cout<< "is available int B.v ? (most be 1) "
        << available_data(B,int,v)     <<endl;

    cout<< "is available int& B.r ?  (most be 1) "
        << available_data(B,int&,r)    <<endl;

    cout<< "is available int B.a[10] ?  (most be 1) "
        << available_data(B,int[10],a) <<endl;

    cout<< "is available int* B.p ?  (most be 1) "
        << available_data(B,int*,p)    <<endl;

    cout<< "is available int* B.pp ?  (most be 1) "
        << available_data(B,int**,pp)  <<endl;

    //---- детектирование методов
    cout<< "is available void B.fooA(int) ?  (most be 1) "
        << can_launch_method(B,void(int),fooA) <<endl;

    cout<< "is available void B.fooA(int)const ?  (most be 1) "
        << can_launch_cmethod(B,void(int),fooA) <<endl;

    cout<< "is available int B.fooB() ?  (most be 1) "
        << can_launch_method(B,int(),fooB) <<endl;

    cout<< "is available void B.private_foo() ?  (most be 0) "    
        << can_launch_method(B,void(),private_foo) <<endl;

    cout<< "is available void B.foo(int) ?  (most be 0) "    
        << can_launch_method(B,void(int),foo) <<endl;
}

#endif
    